package com.xjgz.aio.socket.pro.extension.proessor;

import com.xjgz.aio.socket.MessageProcessor;
import com.xjgz.aio.socket.NetMonitor;
import com.xjgz.aio.socket.StateMachineEnum;
import com.xjgz.aio.socket.pro.extension.plugins.Plugin;
import com.xjgz.aio.socket.transport.AioSession;

import java.nio.channels.AsynchronousSocketChannel;
import java.util.ArrayList;
import java.util.List;

public abstract class AbstractMessageProcessor<T> implements MessageProcessor<T>, NetMonitor {

    private final List<Plugin<T>> plugins = new ArrayList<>();

    @Override
    public void afterRead(AioSession session, int readSize) {
        for (Plugin<T> plugin : plugins) {
            plugin.afterRead(session, readSize);
        }
    }

    @Override
    public void afterWrite(AioSession session, int writeSize) {
        for (Plugin<T> plugin : plugins) {
            plugin.afterWrite(session, writeSize);
        }
    }

    @Override
    public void beforeRead(AioSession session) {
        for (Plugin<T> plugin : plugins) {
            plugin.beforeRead(session);
        }
    }

    @Override
    public void beforeWrite(AioSession session) {
        for (Plugin<T> plugin : plugins) {
            plugin.beforeWrite(session);
        }
    }

    @Override
    public AsynchronousSocketChannel shouldAccept(AsynchronousSocketChannel channel) {
        AsynchronousSocketChannel acceptedChannel = channel;
        for (Plugin<T> plugin : plugins) {
            acceptedChannel = plugin.shouldAccept(acceptedChannel);
            if (acceptedChannel == null) {
                return null;
            }
        }
        return acceptedChannel;
    }

    @Override
    public void process(AioSession session, T message) {
        boolean flag = true;
        for (Plugin<T> plugin : plugins) {
            if (!plugin.preProcess(session, message)) {
                flag = false;
            }
        }
        if (flag) {
            process0(session, message);
        }
    }

    public abstract void process0(AioSession session, T message);

    @Override
    public void stateEvent(AioSession session, StateMachineEnum stateMachineEnum, Throwable throwable) {
        for (Plugin<T> plugin : plugins) {
            plugin.stateEvent(stateMachineEnum, session, throwable);
        }
        stateEvent0(session, stateMachineEnum, throwable);
    }

    public abstract void stateEvent0(AioSession session, StateMachineEnum stateMachineEnum, Throwable throwable);

    public final void addPlugin(Plugin<T> plugin) {
        this.plugins.add(plugin);
    }
}
